package lifi

import (
	"sort"
	"time"
)

// Tag represents a single tag key and value.
type Tag struct {
	Key   string
	Value string
}

// Field represents a single field key and value.
type Field struct {
	Key   string
	Value interface{}
}
type Metric interface {
	Data
	STableName() string

	TName() string

	Tags() map[string]string

	TagList() []*Tag

	Fields() map[string]interface{}

	FieldList() []*Field

	Time() time.Time

	SetTime(t time.Time)

	AddField(key string, value interface{})
	RemoveField(key string)
	HasField(key string) bool
	GetField(key string) (interface{}, bool)

	RemoveTag(key string)
	AddTag(key, value string)
	HasTag(key string) bool
	GetTag(key string) (string, bool)
}
type TemplateMetric interface {
	Tag(key string) string
	Field(key string) interface{}
}

type MetricData struct {
	stableName string
	tname      string
	tags       []*Tag
	fields     []*Field
	time       time.Time
}

func (m *MetricData) Tag(key string) string {
	v, _ := m.GetTag(key)
	return v
}

func (m *MetricData) Field(key string) interface{} {
	v, _ := m.GetField(key)
	return v
}

func (m *MetricData) SetTime(t time.Time) {
	m.time = t
}

func (m *MetricData) RemoveField(key string) {
	for i, field := range m.fields {
		if field.Key == key {
			copy(m.fields[i:], m.fields[i+1:])
			m.fields[len(m.fields)-1] = nil
			m.fields = m.fields[:len(m.fields)-1]
			return
		}
	}
}

func (m *MetricData) HasField(key string) bool {
	for _, field := range m.fields {
		if field.Key == key {
			return true
		}
	}
	return false
}

func (m *MetricData) GetField(key string) (interface{}, bool) {
	for _, field := range m.fields {
		if field.Key == key {
			return field.Value, true
		}
	}
	return nil, false
}

func (m *MetricData) RemoveTag(key string) {
	for i, tag := range m.tags {
		if tag.Key == key {
			copy(m.tags[i:], m.tags[i+1:])
			m.tags[len(m.tags)-1] = nil
			m.tags = m.tags[:len(m.tags)-1]
			return
		}
	}
}

func (m *MetricData) AddTag(key, value string) {
	for i, tag := range m.tags {
		if key > tag.Key {
			continue
		}

		if key == tag.Key {
			tag.Value = value
			return
		}

		m.tags = append(m.tags, nil)
		copy(m.tags[i+1:], m.tags[i:])
		m.tags[i] = &Tag{Key: key, Value: value}
		return
	}

	m.tags = append(m.tags, &Tag{Key: key, Value: value})
}

func (m *MetricData) HasTag(key string) bool {
	for _, tag := range m.tags {
		if tag.Key == key {
			return true
		}
	}
	return false
}

func (m *MetricData) GetTag(key string) (string, bool) {
	for _, tag := range m.tags {
		if tag.Key == key {
			return tag.Value, true
		}
	}
	return "", false
}

func NewMetricData(
	stableName string,
	tname string,
	tags map[string]string,
	fields map[string]interface{},
	time time.Time,
) *MetricData {

	m := &MetricData{
		stableName: stableName,
		tname:      tname,
		tags:       nil,
		fields:     nil,
		time:       time,
	}

	if len(tags) > 0 {
		m.tags = make([]*Tag, 0, len(tags))
		for k, v := range tags {
			m.tags = append(m.tags,
				&Tag{Key: k, Value: v})
		}
		sort.Slice(m.tags, func(i, j int) bool { return m.tags[i].Key < m.tags[j].Key })
	}

	if len(fields) > 0 {
		m.fields = make([]*Field, 0, len(fields))
		for k, v := range fields {
			v := convertField(v)
			if v == nil {
				continue
			}
			m.AddField(k, v)
		}
	}

	return m

}

func (m *MetricData) Group() Group {

	return Group(m.stableName)
}

func (m *MetricData) Drop() {
}

func (m *MetricData) STableName() string {
	return m.stableName
}

func (m *MetricData) TName() string {
	return m.tname
}

func (m *MetricData) Tags() map[string]string {
	tags := make(map[string]string)
	for _, tag := range m.tags {
		tags[tag.Key] = tag.Value
	}
	return tags
}

func (m *MetricData) TagList() []*Tag {
	return m.tags
}

func (m *MetricData) Fields() map[string]interface{} {
	fields := make(map[string]interface{})
	for _, field := range m.fields {
		fields[field.Key] = field.Value
	}
	return fields
}

func (m *MetricData) AddField(key string, value interface{}) {
	for i, field := range m.fields {
		if key == field.Key {
			m.fields[i] = &Field{Key: key, Value: convertField(value)}
			return
		}
	}
	m.fields = append(m.fields, &Field{Key: key, Value: convertField(value)})
}
func (m *MetricData) FieldList() []*Field {
	return m.fields
}

func (m *MetricData) Time() time.Time {
	return m.time
}

var _ Metric = &MetricData{}
var _ Data = &MetricData{}
var _ TemplateMetric = &MetricData{}

func convertField(v interface{}) interface{} {
	switch v := v.(type) {
	case float64:
		return v
	case int64:
		return v
	case string:
		return v
	case bool:
		return v
	case int:
		return int64(v)
	case uint:
		return uint64(v)
	case uint64:
		return v
	case []byte:
		return string(v)
	case int32:
		return int64(v)
	case int16:
		return int64(v)
	case int8:
		return int64(v)
	case uint32:
		return uint64(v)
	case uint16:
		return uint64(v)
	case uint8:
		return uint64(v)
	case float32:
		return float64(v)
	case *float64:
		if v != nil {
			return *v
		}
	case *int64:
		if v != nil {
			return *v
		}
	case *string:
		if v != nil {
			return *v
		}
	case *bool:
		if v != nil {
			return *v
		}
	case *int:
		if v != nil {
			return int64(*v)
		}
	case *uint:
		if v != nil {
			return uint64(*v)
		}
	case *uint64:
		if v != nil {
			return *v
		}
	case *[]byte:
		if v != nil {
			return string(*v)
		}
	case *int32:
		if v != nil {
			return int64(*v)
		}
	case *int16:
		if v != nil {
			return int64(*v)
		}
	case *int8:
		if v != nil {
			return int64(*v)
		}
	case *uint32:
		if v != nil {
			return uint64(*v)
		}
	case *uint16:
		if v != nil {
			return uint64(*v)
		}
	case *uint8:
		if v != nil {
			return uint64(*v)
		}
	case *float32:
		if v != nil {
			return float64(*v)
		}
	default:
		return nil
	}
	return nil
}
